package com.decrypt;

import org.apache.commons.codec.binary.Base64;
import reactor.util.function.Tuple2;
import reactor.util.function.Tuples;

import javax.crypto.Cipher;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.*;

/**
 * RSA 工具类
 * 单例模式
 *
 * @author: linlong_kzx
 * @date: 2021/03/16
 */
public class RSAUtil {
    /**
     * 实例化单例
     */
    private static RSAUtil security = new RSAUtil();

    /**
     * 不允许外部实例化
     */
    private RSAUtil() {
        super();
    }

    /**
     * 获取单例
     *
     * @return
     */
    public static RSAUtil getInstance() {
        return security;
    }

    private static KeyPairGenerator keyPairGenerator;

    {
        try {
            keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            // 注意：2048位 / (8位/字节) = 256字节 - 11字节 = 245 字节，加密内容长度不能超过245字节
            keyPairGenerator.initialize(2048);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
    }

    /**
     * 签名套件
     *
     * @author Aaron
     */
    public static enum SignatureSuite {

        SHA1("SHA1WithRSA"),
        SHA256("SHA256WithRSA");

        private String suite;

        SignatureSuite(String suite) {
            this.suite = suite;
        }

        public String val() {
            return suite;
        }
    }

    /**
     * @return PubKey & PrvKey
     */
    public Tuple2<String, String> generateKeyPair() {
        // 初始化
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        // PubKey
        RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
        // PrvKey
        RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
        // 返回
        return Tuples.of(new String(Base64.encodeBase64(rsaPublicKey.getEncoded())), new String(Base64.encodeBase64(rsaPrivateKey.getEncoded())));
    }

    /**
     * @return KeyFactory
     */
    private KeyFactory getKeyFactory() {
        try {
            return KeyFactory.getInstance("RSA");
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("初始化RSA KeyFactory失败");
        }
    }

    /**
     * PubKey加密
     *
     * @param rsaPublicKey PubKey
     * @param src          源字符串
     * @return 密文
     */
    public String encrypt(String rsaPublicKey, String src) {
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(rsaPublicKey));
        KeyFactory keyFactory;
        try {
            keyFactory = this.getKeyFactory();
            PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
            // 初始化加密
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            // 加密字符串
            byte[] result = cipher.doFinal(src.getBytes());
            return new String(Base64.encodeBase64(result));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * PrvKey解密
     *
     * @param rsaPrivateKey PrvKey
     * @param ciphertext    密文
     * @return 明文
     */
    public String decrypt(String rsaPrivateKey, String ciphertext) {
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(rsaPrivateKey));
        KeyFactory keyFactory;
        try {
            keyFactory = this.getKeyFactory();
            PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
            // 初始化解密
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            // 解密字符串
            byte[] result = cipher.doFinal(Base64.decodeBase64(ciphertext));
            return new String(result);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * PrvKey加签
     *
     * @param suite
     * @param srcBuf
     * @param privateKeyStr
     * @return 签名字节数组
     */
    public byte[] sign(SignatureSuite suite, byte[] srcBuf, String privateKeyStr) {
        Signature signature = null;
        try {
            signature = Signature.getInstance(suite.val());
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("NO_SUCH_ALGORITHM");
        }
        try {
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyStr));
            PrivateKey privateKey = this.getKeyFactory().generatePrivate(keySpec);
            signature.initSign(privateKey);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("INVALID_PRIKEY");
        }
        try {
            signature.update(srcBuf);
            return signature.sign();
        } catch (SignatureException e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    /**
     * PrvKey加签
     *
     * @param suite
     * @param src
     * @param privateKeyStr
     * @return 签名字符串
     */
    public String sign(SignatureSuite suite, String src, String privateKeyStr) {
        try {
            return Base64.encodeBase64String(this.sign(suite, src.getBytes("UTF-8"), privateKeyStr));
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("不支持的编码");
        }
    }

    /**
     * PubKey验签
     *
     * @param suite
     * @param srcBuf
     * @param sign
     * @param publicKeyStr
     * @return
     */
    public boolean verifySign(SignatureSuite suite, byte[] srcBuf, byte[] sign, String publicKeyStr) {
        Signature signature = null;
        try {
            signature = Signature.getInstance(suite.val());
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("NO_SUCH_ALGORITHM");
        }
        try {
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyStr));
            PublicKey publicKey = getKeyFactory().generatePublic(keySpec);
            signature.initVerify(publicKey);
        } catch (Exception e) {
            throw new RuntimeException("INVALID_PUBKEY");
        }
        try {
            signature.update(srcBuf);
            return signature.verify(sign);
        } catch (SignatureException e) {
            throw new RuntimeException("签名格式不合法");
        }
    }

    /**
     * 过滤参数
     *
     * @param params
     * @return
     */
    public Map<String, String> paramFilter(Map<String, String> params) {
        Map<String, String> result = new HashMap<>(params.size());
        if (params == null || params.size() <= 0) {
            return result;
        }
        for (String key : params.keySet()) {
            String value = params.get(key);
            if (value == null || value.equals("") || key.equalsIgnoreCase("sign")) {
                continue;
            }
            result.put(key, value);
        }
        return result;
    }

    /**
     * 构建参数字符串，按参数键升序排列
     *
     * @param params
     * @param encoding
     * @return
     */
    public String buildParamsString(Map<String, String> params, boolean encoding) {
        StringBuilder sb = new StringBuilder();
        List<String> keys = new ArrayList<String>(params.keySet());
        Collections.sort(keys);
        for (String key : keys) {
            sb.append(key).append("=");
            if (encoding) {
                sb.append(urlEncode(params.get(key)));
            } else {
                sb.append(params.get(key));
            }
            sb.append("&");
        }
        sb.setLength(sb.length() - 1);
        return sb.toString();
    }

    /**
     * 参数编码
     *
     * @param param
     * @return
     */
    public String urlEncode(String param) {
        try {
            return URLEncoder.encode(param, "utf-8");
        } catch (Throwable e) {
            return param;
        }
    }

    /**
     * 参数解码
     *
     * @param param
     * @return
     */
    public String urlDecoder(String param) {
        try {
            return URLDecoder.decode(param, "utf-8");
        } catch (Throwable e) {
            return param;
        }
    }

    /**
     * 获取测试PubKey
     *
     * @return
     */
    public String getTestPublicKey() {
        return "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAinWNjcKx3CCf0Q/ClHPFlcOhcZrnZly5ZPFFV3DF/HsOSOTPf0KGXHvsApynAds5LUdUzdSpzN/ko+Sxkizz6n1jqS4FYblCmZJNqtIypAqasLXZSibQGnaDVFVIGrfD7eip3hopPVU7OuzLj5ku5nDztTZzEpqj4hFNoQWwwONBC8Xc0lUdjTaIo/GZ/ICVL05A/CG/C4G9inMAEbCK4lVSa63UtryigeX3vDtQ14XaDp7pby0HDgwr2BnbloPACBG5sEklnVfzKyeDKQHSY1BuiXtrikx7hRcjVqRFghgnWBOJUHt1w7bUomeSQk2kWMLpk+0mgQz6AX7EF+FLowIDAQAB";
    }

    /**
     * 获取测试PrvKey
     *
     * @return
     */
    public String getTestPrivateKey() {
        return "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCKdY2NwrHcIJ/RD8KUc8WVw6FxmudmXLlk8UVXcMX8ew5I5M9/QoZce+wCnKcB2zktR1TN1KnM3+Sj5LGSLPPqfWOpLgVhuUKZkk2q0jKkCpqwtdlKJtAadoNUVUgat8Pt6KneGik9VTs67MuPmS7mcPO1NnMSmqPiEU2hBbDA40ELxdzSVR2NNoij8Zn8gJUvTkD8Ib8Lgb2KcwARsIriVVJrrdS2vKKB5fe8O1DXhdoOnulvLQcODCvYGduWg8AIEbmwSSWdV/MrJ4MpAdJjUG6Je2uKTHuFFyNWpEWCGCdYE4lQe3XDttSiZ5JCTaRYwumT7SaBDPoBfsQX4UujAgMBAAECggEABW+aEB+9teO+Aoo43HUzI68bjodDYHxxduoWC9nMnB4EsI3zK47kEYt3955I3ETMGN/9D1uzEu0TX+/hnNiQ9vrXR9Qbt/t7BuZ8kCFShag47IknL8cYUoGmpE/41buzQIrq6GJkjhjji2uyjj+4XKnAl7O8jtVta7m8L382skvq/wtNBc+vkgtTvAT5TFhba0XB1q3n6Xd7pm1hxSxXSgLtoLwf6K+Q8FWj/zk7ODbyBkBZKlAax9lHYmfUD/dZoGhGSPG8R9IADUB//8xOX5JSWSdDMG5Op+/E04DsLiCH/cEoTyM4z46WqIslH30w8vohh4SCKLfddyKu47Cg8QKBgQDBO/c9/ywiOkA+odF1lLmtlA5vOrqk5QZ49aYUFwpDbnoT8jeSV7NrMeyiC2GPnUwIFJZDZ5hY1ddzjx+rbM5ofzIQxq59hyoNpHjqmCz1aPvT2MB3tn+NPP0IUoVLF4lZ4DXnxyzOT40licFS/QMnOpI8ATN7qiLEf3xVwXlyuwKBgQC3bt6h5eTjq9Ixy+4Qkv3y4t40NjwpPIexb/v2kd7YexkQ8Ht36xPMw24CU+ZYtq1oyomm2/mojZEHHcaA+R0nzSMMYq/tFKivkynYT+43oTaqEJUcQLKn0Kp++zEEqMVQX/ji4AOKbFzNh06gHY+xiPCnCpzYlaTXyKLQZvhAOQKBgFE8hJTr0gIRuzCZlxV3kRgIRok8h3sLTc1MeKW5ifcPWwNHsVU0yZknQ/RcFIfr+SrsfL+ZrdKrFAjhAs9uztffoYyr+m+Lg4J4wtzdXQlPOY+2cDPj5XvbSAVXq/IF0xiVyhPHCG8zwoMlhsD83XaAiTnwx1QWEsfJUNw8mnJBAoGAKFoibbkWFiOJNN0ovMqpRNI1kYTQzT18mEMLSZ2jSTojNH/CmYnELEpdm/N6+88L7L6Jc4R2smaaHdemAF9M06Gj8SiQjPX7ecxE60oCHIjBbr4BH1r4MkSMDS0TBnReG6VRKg+fl8boz2nRmRWHs8VtM0H5aQfaYIHJl1DfkbkCgYB+C48sgd2HjcFwhBUnAAiqbIT5XvGeA8NYHsJUwotOvrH++lKQdQXeIr8MrYuKKaGFs488ZiM8X0i2weebImw+tYD1+zAmNfYda20E6EFQStJMPzikfaZbMR1H6YScHYkk94WaafiUdgPFNzSw3rqUvEnbFzfEtySyTH/oWmFgMg==";
    }

    public static void main(String[] args) throws UnsupportedEncodingException {
        RSAUtil rsaUtil = RSAUtil.getInstance();
//        System.out.println("===============================");
//        String publicKey = rsaUtil.getTestPublicKey();
//        String privateKey = rsaUtil.getTestPrivateKey();

        String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCFHxVO/OKnX04Gn2S0y9iYQSFDqD9JZ6GKN4EoHGpNL+wwiPrv8y+GrBG27JQrLHVjtPVZPYDThzjMA1YyHNZirI2nBmxsCyjpN2hGoc9oelPIreLTPGd5R0LQZeVg2Xjf0izJOT+NiY8ARPZr3MISbwVdHON0ivBBn9mh/dD7gwIDAQAB";
        String privateKey = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAIUfFU784qdfTgafZLTL2JhBIUOoP0lnoYo3gSgcak0v7DCI+u/zL4asEbbslCssdWO09Vk9gNOHOMwDVjIc1mKsjacGbGwLKOk3aEahz2h6U8it4tM8Z3lHQtBl5WDZeN/SLMk5P42JjwBE9mvcwhJvBV0c43SK8EGf2aH90PuDAgMBAAECgYBJVoN9vZH8lWO+AEgR1o+f6Zmy1VjwTrhPFh8Agw8JMRKU9pIQaVBQiDFGLnW+h0KwyHu2S0zSvBdNRbVR7FMqwH1KFZmxis4S6fG9p1rJGswEFErL0pcVjbgWejZ3ob2UQPZEkfMDXr2StybBDNO6hJ8ktDmJKD37duthZQVkIQJBAN3cxGUKre1/uMl272TDDQiyikOv/UZxTxdcF0mw3lK0eeh50EdlSpVJBCCoHZg0KTZkJRdpIKRDMCGDt0+W0xECQQCZmsfD+kON4kTmP586GHUSVzh4ixxfhHnOlJ8g8vIdOYDx4oYxERUdximZu/jUwQYDUbf3Mi4NzqEh0+7Gp71TAkAGPvHUemF9Hyxfl5aTup4CUaxszWaO9h+o47HQJEeOAoCwF8XSTLq1yzFvjewPXeCPJHCa4R3HIhEX0HJ3riJRAkAJ8dZrHsHO5s5VJdtWNI0n0dPLIh6FJJuKP18KEBCSyc1KW8MaXCPE2LaKyHu0soaEIn9Jlssag7005na4D9YXAkBFN5GyKXjEWs6YxaTpMElYP4mV+2X6XspuBQlVOaM47NpaCmOpi6qtCd6qmDWHx6LIrD8KNtBjCSEbnROVx92j";


//        String src = "";
//        String ciphertext = rsaUtil.encrypt(publicKey, src);
//        System.out.println(src+" 密文："+ciphertext);
//        String result = rsaUtil.decrypt(privateKey, ciphertext);
//        System.out.println(src+" 明文："+result);

        Map<String, String> params = new HashMap<>();
        params.put("marketingID", "500001");
        params.put("custMblphNoLast4", "8355");
//        params.put("informType", "1");
//        params.put("informFlow", "202201231521098731184");
//        String timestamp = String.valueOf(System.currentTimeMillis());
//        System.out.println("timestamp:" + timestamp);
//        params.put("timestamp", timestamp);
//        params.put("nonce", "666666");
//        params.put("echostr", "abcdef");

        String msg = rsaUtil.buildParamsString(params, false);
        System.out.println("消息摘要，msg=" + msg);

        String sign = rsaUtil.sign(SignatureSuite.SHA256, msg, privateKey);
        System.out.println("参数签名，未编码：" + sign);

        sign = RSAUtil.getInstance().urlEncode(sign);
        System.out.println("参数签名，编码后：" + sign);

        params.put("sign", sign);

        System.out.println(sign);
//        System.out.println("参数验签：" + rsaUtil.verifySign(SignatureSuite.SHA256,
//                RSAUtil.getInstance().buildParamsString(RSAUtil.getInstance().paramFilter(params), false).getBytes(),
//                Base64.decodeBase64(rsaUtil.urlDecoder(sign).getBytes("utf-8")), publicKey));
    }
}